package com.example.appbarlayoutdemo.weight;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.OverScroller;

import androidx.coordinatorlayout.widget.CoordinatorLayout;

import com.google.android.material.appbar.AppBarLayout;

import java.lang.reflect.Field;
import java.util.Objects;


/**
 * 作者:  njb
 * 时间:  2019/4/30 12:38
 * 描述:
 */

public class CustomBehavior extends AppBarLayout.Behavior {
    private static final String TAG = "CustomAppbarLayoutBehavior";


    private static final int TYPE_FLING = 1;


    private boolean isFlinging;

    private boolean shouldBlockNestedScroll;


    public CustomBehavior(Context context, AttributeSet attrs) {

        super(context, attrs);

    }


    @SuppressLint("LongLogTag")
    @Override

    public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {

        Log.d(TAG, "onInterceptTouchEvent:" + child.getTotalScrollRange());

        shouldBlockNestedScroll = false;

        if (isFlinging) {

            shouldBlockNestedScroll = true;

        }


        switch (ev.getActionMasked()) {

            case MotionEvent.ACTION_DOWN:

                stopAppbarLayoutFling(child);  //手指触摸屏幕的时候停止fling事件

                break;

        }


        return super.onInterceptTouchEvent(parent, child, ev);

    }


    /**
     * 反射获取私有的flingRunnable 属性，考虑support 28以后变量名修改的问题
     *
     * @return Field
     * @throws
     */

    private Field getFlingRunnableField() throws NoSuchFieldException {

        try {

            // support design 27及一下版本

            Class<?> headerBehaviorType = Objects.requireNonNull(this.getClass().getSuperclass()).getSuperclass();

            assert headerBehaviorType != null;
            return headerBehaviorType.getDeclaredField("mFlingRunnable");

        } catch (NoSuchFieldException e) {

            e.printStackTrace();

            // 可能是28及以上版本

            Class<?> headerBehaviorType = Objects.requireNonNull(this.getClass().getSuperclass().getSuperclass()).getSuperclass();

            assert headerBehaviorType != null;
            return headerBehaviorType.getDeclaredField("flingRunnable");

        }

    }


    /**
     * 反射获取私有的scroller 属性，考虑support 28以后变量名修改的问题
     *
     * @return Field
     * @throws
     */

    private Field getScrollerField() throws NoSuchFieldException {

        try {

            // support design 27及一下版本

            Class<?> headerBehaviorType = Objects.requireNonNull(this.getClass().getSuperclass()).getSuperclass();

            assert headerBehaviorType != null;
            return headerBehaviorType.getDeclaredField("mScroller");

        } catch (NoSuchFieldException e) {

            e.printStackTrace();

            // 可能是28及以上版本

            Class<?> headerBehaviorType = Objects.requireNonNull(this.getClass().getSuperclass().getSuperclass()).getSuperclass();

            assert headerBehaviorType != null;
            return headerBehaviorType.getDeclaredField("scroller");

        }

    }


    /**
     * 停止appbarLayout的fling事件
     *
     * @param appBarLayout
     */

    private void stopAppbarLayoutFling(AppBarLayout appBarLayout) {

        //通过反射拿到HeaderBehavior中的flingRunnable变量

        try {

            Field flingRunnableField = getFlingRunnableField();

            Field scrollerField = getScrollerField();

            flingRunnableField.setAccessible(true);

            scrollerField.setAccessible(true);


            Runnable flingRunnable = (Runnable) flingRunnableField.get(this);

            OverScroller overScroller = (OverScroller) scrollerField.get(this);

            if (flingRunnable != null) {

                Log.d(TAG, "存在flingRunnable");

                appBarLayout.removeCallbacks(flingRunnable);

                flingRunnableField.set(this, null);

            }

            if (overScroller != null && !overScroller.isFinished()) {

                overScroller.abortAnimation();

            }

        } catch (NoSuchFieldException e) {

            e.printStackTrace();

        } catch (IllegalAccessException e) {

            e.printStackTrace();

        }

    }


    @Override

    public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes, int type) {

        Log.d(TAG, "onStartNestedScroll");

        stopAppbarLayoutFling(child);

        return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type);

    }


    @Override

    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {

        Log.d(TAG, "onNestedPreScroll:" + child.getTotalScrollRange() + " ,dx:" + dx + " ,dy:" + dy + " ,type:" + type);


        //type返回1时，表示当前target处于非touch的滑动，

        //该bug的引起是因为appbar在滑动时，CoordinatorLayout内的实现NestedScrollingChild2接口的滑动子类还未结束其自身的fling

        //所以这里监听子类的非touch时的滑动，然后block掉滑动事件传递给AppBarLayout

        if (type == TYPE_FLING) {

            isFlinging = true;

        }

        if (!shouldBlockNestedScroll) {

            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);

        }

    }


    @Override

    public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dxConsumed, int dyConsumed, int

            dxUnconsumed, int dyUnconsumed, int type) {

        Log.d(TAG, "onNestedScroll: target:" + target.getClass() + " ," + child.getTotalScrollRange() + " ,dxConsumed:"

                + dxConsumed + " ,dyConsumed:" + dyConsumed + " " + ",type:" + type);

        if (!shouldBlockNestedScroll) {

            super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);

        }

    }


    @Override

    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target, int type) {

        Log.d(TAG, "onStopNestedScroll");

        super.onStopNestedScroll(coordinatorLayout, abl, target, type);

        isFlinging = false;

        shouldBlockNestedScroll = false;

    }
}
